package com.atguigu.gulimall.ware.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.enums.OrderStatusEnum;
import com.atguigu.common.to.order.OrderTo;
import com.atguigu.common.to.ware.WareOrderTaskDetailTO;
import com.atguigu.common.to.ware.WareSkuLockedTO;
import com.atguigu.common.utils.R;
import com.atguigu.common.utils.StringUtils;
import com.atguigu.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimall.ware.exceptional.NoWareException;
import com.atguigu.gulimall.ware.feign.OrderFeignService;
import com.atguigu.gulimall.ware.feign.SkuInfoFeignService;
import com.atguigu.gulimall.ware.rabbit.constant.WareConstant;
import com.atguigu.gulimall.ware.response.CartItemResp;
import com.atguigu.gulimall.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimall.ware.service.WareOrderTaskService;
import com.atguigu.gulimall.ware.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.ware.dao.WareSkuDao;
import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import com.atguigu.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

@Slf4j
@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    WareSkuDao wareSkuDao;
    
    @Autowired
    SkuInfoFeignService skuInfoFeignService;
    
    @Autowired
    WareOrderTaskService wareOrderTaskService;
    
    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;
    
    @Autowired
    RabbitTemplate rabbitTemplate;
    
    @Autowired
    OrderFeignService orderFeignService;
    
    
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageUtils(page);
    }
    
    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        //1、查询库存信息是否存在
        List<WareSkuEntity> wareSkuEntityList = this.list(new LambdaQueryWrapper<WareSkuEntity>().eq(WareSkuEntity::getSkuId, skuId).eq(WareSkuEntity::getWareId, wareId));
        if (CollectionUtils.isEmpty(wareSkuEntityList)) {
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setWareId(wareId);
            wareSkuEntity.setStock(skuNum);
            //远程调用product服务获取到skuName
            try {
                R skuInfoResult = skuInfoFeignService.info(skuId);
                if (skuInfoResult.getCode() == 0) {
                    Map<String, Object> skuMap = (Map<String, Object>) skuInfoResult.get("skuInfo");
                    String skuName = (String) skuMap.get("skuName");
                    wareSkuEntity.setSkuName(skuName);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            this.save(wareSkuEntity);
        }else {
            wareSkuDao.addStock(skuId, wareId, skuNum);
        }
    
    }
    
    @Override
    public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds) {
        return skuIds.stream().map(skuId -> {
            Long count = baseMapper.getSkuStock(skuId);
            SkuHasStockVo skuHasStockVo = new SkuHasStockVo();
            skuHasStockVo.setSkuId(skuId);
            skuHasStockVo.setHasStock(count != null && count > 0);
            return skuHasStockVo;
        }).collect(Collectors.toList());
    }

    /**
     * 锁定库存
     * @param wareSkuLockVO 
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean orderItemLock(WareSkuLockVO wareSkuLockVO) {
//        log.info("OrderItemLock receive request params: {}", JSONObject.toJSONString(wareSkuLockVO));
        Long orderTaskId = saveOrderTask(wareSkuLockVO);
        //查询该sku附近的仓库
        List<CartItemResp> orderItems = wareSkuLockVO.getOrderItems();
        List<SkuWareResultVO> skuWareResultVOList = orderItems.stream().map(orderItem -> {
            Long skuId = orderItem.getSkuId();
            List<Long> wareIds = wareSkuDao.findWareBySkuId(skuId);
            if (wareIds == null || wareIds.isEmpty()) {
                String msg = String.format("%s%s%s", "仓库中没有", skuId, "号商品");
                throw new NoWareException(msg);
            }
            return buildSkuWareResult(orderItem, skuId, wareIds);
        }).collect(Collectors.toList());
        //锁定库存
        for (SkuWareResultVO skuWareResultVO : skuWareResultVOList) {
            boolean locked = false;
            Long skuId = skuWareResultVO.getSkuId();
            List<Long> wareIds = skuWareResultVO.getWareIds();
            for (Long wareId : wareIds) {
                Integer lockedResult = wareSkuDao.skuLockedWare(skuId, wareId, skuWareResultVO.getSkuNum());
                if (lockedResult == 0) {
                    locked = true;
                    break;
                }
                //库存锁定成功,保存工作单详情
                WareOrderTaskDetailEntity wareOrderTaskDetail = new WareOrderTaskDetailEntity();
                wareOrderTaskDetail.setSkuId(skuWareResultVO.getSkuId());
                wareOrderTaskDetail.setSkuName("");
                wareOrderTaskDetail.setTaskId(orderTaskId);
                wareOrderTaskDetail.setWareId(wareId);
                wareOrderTaskDetail.setLockStatus(1);
                wareOrderTaskDetail.setSkuNum(skuWareResultVO.getSkuNum());
                wareOrderTaskDetailService.save(wareOrderTaskDetail);
                WareOrderTaskDetailTO wareOrderTaskDetailTo = new WareOrderTaskDetailTO();
                BeanUtils.copyProperties(wareOrderTaskDetail, wareOrderTaskDetailTo);
                WareSkuLockedTO wareSkuLockedTo = WareSkuLockedTO.builder()
                        .orderSn(wareSkuLockVO.getOrderSn())
                        .wareOrderTaskDetailTo(wareOrderTaskDetailTo)
                        .build();
                //发送锁定成功消息
                rabbitTemplate.convertAndSend(WareConstant.STOCK_EVENT_EXCHANGE, WareConstant.STOCK_DELAY_ROUTING_KEY, wareSkuLockedTo);
            }
            //判断是否锁定成功
            if (locked) {
                String msg = String.format("%s%s%s", "仓库中没有", skuId, "号商品");
                throw new NoWareException(msg); 
            }
        }
        //返回结果
        return true;
    }
    

    private Long saveOrderTask(WareSkuLockVO wareSkuLockVO) {
        //参数非空校验
        if (wareSkuLockVO == null) {
            throw new IllegalArgumentException("wareSkuLockVo cannot be null");
        }
        String orderSn = wareSkuLockVO.getOrderSn();
        if (StringUtils.isEmpty(orderSn)) {
            throw new IllegalArgumentException("orderSn cannot be null or empty");
        }
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(orderSn);
        try {
            wareOrderTaskService.save(wareOrderTaskEntity);
            return wareOrderTaskEntity.getId();
        } catch (Exception e) {
            throw new RuntimeException("Field to save ware order task", e);
        }
    }

    /**
     * 释放锁定的库存。
     * 此方法用于处理订单取消或其他需要释放之前锁定的库存的情况。
     * 需要解锁库存的情况
     * 1. 订单库存锁定成功，调用其他服务失败此时订单回滚库存锁定成功，需要使用MQ进行补偿
     * 2. 库存锁定失败，此时订单也会回滚无需进行解锁库存
     * 3. 订单创建成功，但是没有成功支付此时订单状态为取消状态，次数进行库存解锁
     *
     * @param wareSkuLockedTo 包含锁定库存详细信息的对象。此对象应包含订单号（orderSn）和仓库订单任务详情（wareOrderTaskDetailTo）。
     *                        通过这些信息，可以找到并释放与特定订单相关的锁定库存。
     * @return void 此方法没有返回值，它的主要目的是更新库存状态。
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void releaseStockLocked(WareSkuLockedTO wareSkuLockedTo) {
        String orderSn = wareSkuLockedTo.getOrderSn();
        WareOrderTaskDetailTO wareOrderTaskDetailTo = wareSkuLockedTo.getWareOrderTaskDetailTo();
        WareOrderTaskDetailEntity wareOrderTaskDetail = wareOrderTaskDetailService.getById(wareOrderTaskDetailTo.getId());
        //判断库存是否锁定成功，如果锁定失败无需解锁
        if (Objects.isNull(wareOrderTaskDetail)) {
            log.info("releaseStockLocked WareOrderTaskDetail is null no need release ware stock wareOrderTaskDetail : {}", JSONObject.toJSONString(wareOrderTaskDetail));
            return;
        }
        R orderResult = orderFeignService.getOrderByOrderSn(orderSn);
        //订单服务调用失败
        if (orderResult == null || orderResult.getCode() != 0) {
            throw new RuntimeException("订单服务调用失败，订单sn: " + orderSn);
        }
        //根据订单状态查看是否需要解锁库存
        OrderResp orderResultData = orderResult.getData(new TypeReference<OrderResp>() {
        });
        if (orderResultData == null || OrderStatusEnum.CANCLED.getCode().equals(orderResultData.getStatus())) {
            //订单为取消状态，进行解锁库存
            boolean unlocked = unlockStock(wareOrderTaskDetail.getSkuId(), wareOrderTaskDetail.getWareId(), wareOrderTaskDetail.getSkuNum());
            if (!unlocked) {
                throw new RuntimeException("ware sku stock unlocked field");
            }
            //解锁成功后将工作单详情更新为已解锁状态
            boolean updated = wareOrderTaskDetailService.updateLockedStatus(wareOrderTaskDetail.getId(), 2);
            if (!updated) {
                throw new RuntimeException("库存工作单更新失败！");
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void orderLeaseUnLockStock(OrderTo order) {
        if (Objects.isNull(order) || StringUtils.isEmpty(order.getOrderSn())) {
            throw new RuntimeException("订单或流水号不存在！");
        }
        //根据订单流水号获取订单工作单
        WareOrderTaskEntity orderTask = wareOrderTaskService.getOrderTaskByOrderSn(order.getOrderSn());
        if (Objects.isNull(orderTask)) {
            throw new RuntimeException("订单工作单不存在！");
        }
        //查询工作单详情
        List<WareOrderTaskDetailEntity> orderTaskDetailEntities = wareOrderTaskDetailService.getOrderTaskDetailsByTaskId(orderTask.getId(), 1);
        if (CollectionUtils.isEmpty(orderTaskDetailEntities)) {
            throw new RuntimeException("订单工作单详情不存在！");
        }
        List<WareOrderTaskDetailEntity> orderTaskDetailEntityList = new ArrayList<>();
        //解锁库存
        orderTaskDetailEntities.forEach(orderTaskDetail -> {
            boolean b = unlockStock(orderTaskDetail.getSkuId(), orderTaskDetail.getWareId(), orderTaskDetail.getSkuNum());
            Assert.isTrue(b, "库存解锁失败！");

            WareOrderTaskDetailEntity wareOrderTaskDetail = new WareOrderTaskDetailEntity();
            wareOrderTaskDetail.setId(orderTaskDetail.getId());
            wareOrderTaskDetail.setLockStatus(2);
            orderTaskDetailEntityList.add(wareOrderTaskDetail);
        });

        boolean b = wareOrderTaskDetailService.updateBatchById(orderTaskDetailEntityList);
        Assert.isTrue(b, "库存工作单更新失败！");
    }

    private boolean unlockStock(Long skuId, Long wareId, Integer skuNum) {
        Integer i = wareSkuDao.unLockStock(skuId, wareId, skuNum);
        return i > 0;
    }

    /**
     * 构建库存Vo
     * @param orderItem
     * @param skuId
     * @param wareIds
     * @return
     */
    private static SkuWareResultVO buildSkuWareResult(CartItemResp orderItem, Long skuId, List<Long> wareIds) {
        return SkuWareResultVO.builder()
                .skuId(skuId)
                .wareIds(wareIds)
                .skuNum(orderItem.getCount())
                .build();
    }

}